原文链接 Webpack 4 — Mysterious SplitChunks Plugin
Webpack-4 正式版夸赞了它更快的构建速度(大约 98%)和更小的 chunk 体积。
然而 Webpack 的作者在开发者社区丢了一颗炸弹,他发布了一则关于负责跨多入口代码拆分的插件的公告。文档中 CommonsChunkPlugin 部分写道:
https://webpack.js.org/plugins/commons-chunk-plugin/
以下是我的一次简单的尝试,通过举一个常见的例子来理解并帮助你使用 SplitChunksPlugin chunks 的配置选项。
作为一个早期的爱好者我曾试图理解代码拆分背后的魔法。文档说 “splitChunks.chunks” 选项有三个可选值:“initial”、“async” 和 “all”。我有点困惑,也更加激发了我的好奇心!
我深入挖掘 Github history 和 WebpackOptions Schema 的文档找到了这些内容:
“三个可选值:initial、async 和 all”,配置优化时只能选择 初始块、按需块或所有块 —— Github History
“选择用于确定共享模块的块(默认 “async”、“initial” 或 “all” 需要将这些块添加至 HTML)” —— WebpackOptions Schema
举个例子,有两个导入相同 node_modules 的入口文件 a.js 和 b.js,将其中一些模块动态导入来检查代码拆分的行为。
我们用 webpack-bundle-analyzer 这个插件来帮助我们理解模块是如何被拆分的。
a.js :
只有 lodash 动态导入
1 | import "react"; |
b.js :
React 和 lodash 动态导入
1 | import("react"); // dynamic import |
我选这个配置的主要原因是来理解当存在公共库的时候 Webpack 的配置是如何表现的,
- React - 一个入口动态导入另一个非动态导入
- lodash - 两个入口都动态导入
- jquery - 两个入口都非动态导入
我们保持这些文件不变,改变 Webpack 配置中 chunks
的值。
1. chunks: “async” —— 优化动态模块
1 | var entry = { |
webpack.config.js
BundleAnalyzer 截图
chunks: 'async'
告诉 webpack,
“嘿,webpack!我只关心动态导入的模块的优化,别管非动态模块。”
现在我们一步步看看发生了什么:
- Webpack 把
react
从b.js
里拎出来放到一个新文件里,a.js
里的不动。优化只发生在动态模块上,import("react")
这句声明会导致拆分文件,而import "react"
不会。 - Webpack 把
lodash
从a.js
里拎出来放到一个新文件里,这个新文件也被b.js
引用。 - 尽管
a.js
和b.js
都引用了jquery
但是并不会对它进行优化。为啥?(提示:‘Async’)
2. chunks: “initial” —— 优化同步模块
1 | var entry = { |
webpack.config.js
BundleAnalyzer 截图
chunks: 'initial'
告诉 webpack,
“嘿,webpack!我不关心动态导入的模块,你可以把它们每个都拆成单独的文件。然而我希望把所有非动态导入的模块打在一个包里,尽管我准备好跟其他文件共享并且分块这些非动态导入的模块,如果这些文件也需要非动态导入的模块的话。”
我们再一步步看看发生了什么:
a.js
里的react
将被移到node_vendors~a.bundle.js
而b.js
里的react
将被移到它单独的包0.bundle.js
里。a.js
和b.js
里的lodash
将被移到1.bundle.js
里。为啥?这个lodash
是个动态导入的模块。jquery
作为一个非动态导入的公共模块将在node_vendors~a~b.bundle.js
中被a.js
和b.js
共享。
3. chunks: “all” —— 优化同步和异步模块
1 | var entry = { |
webpack.config.js
BundleAnalyzer 截图
chunks: 'all'
告诉 webpack,
“嘿,webpack!我不管这个模块是动态还是非动态导入的,把它们全部优化掉。但是要确保……呐,你够聪明去干这活儿!”
我们继续一步步看看发生了什么:
react
在a.js
中非动态导入,在b.js
中动态导入。所以它转到一个单独的文件0.bundle.js
中被两个文件引用。lodash
在两个文件中都是动态导入的,所以显然得到一个单独的文件1.bundle.js
。jquery
在两个文件中都是非动态导入的,所有它转到公共的共享模块node_vendors~a~b.bundle.js
,被两个文件引用。
最后
# 译注
看到最后发现还是没讲为什么这么干,但是大致可以猜测是个什么样的规则,可能还是得去官方源码中找答案…… TODO!